package mutt.server;

import com.google.appengine.repackaged.com.google.common.collect.Lists;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import mutt.client.CalcService;
import mutt.shared.FieldVerifier;
import mutt.shared.HashMapBuilder;
import org.jruby.RubyArray;

import javax.script.ScriptException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * The server side implementation of the RPC service.
 * 
 * @author Margus Martsepp
 * @author Sander Medri
 */
@SuppressWarnings("serial")
public class CalcServiceImpl extends RemoteServiceServlet implements
		CalcService {

    private HashMap<String, String> helpLinks;

    public static final String HELP_LINKS_PATH = "/WEB-INF/help/helpLinks.csv";
    public static final String RUBY_LIBS_PATH = "/WEB-INF/rubyLibs/Libz.rb";

    /**
     * Checks if user input is valid, evaluates the specified expression and returns calculation results
     * 
     * @param input - user's input
     * @return the result of the evaluation
     * @throws IllegalArgumentException if the user input is invalid
     * @see CalculatorEngine
     */
	public String doCalc(String input) throws IllegalArgumentException {

		if (!FieldVerifier.isValidInput(input)) {
			throw new IllegalArgumentException(
					"Query must be at least 1 characters long");
		}
        
		CalculatorEngine calculatorEngine = new CalculatorEngine();
        loadLibraries(calculatorEngine);

        StringBuilder stringBuilder = new StringBuilder();
        Object result = null;

        try {
            result = calculatorEngine.eval(input);
            if (result instanceof RubyArray) {
                result = calculatorEngine.eval(input + ".join('<br />')");
    		}
        } catch (ScriptException e) {
            throw new IllegalArgumentException(e.getStackTrace().toString());
        } finally {
            calculatorEngine = null;
        }

        if (result == null) {
            throw new IllegalArgumentException("I have nothing to return.");
        }

        getLinks(input, stringBuilder);
        stringBuilder.append(result.toString());
     
        return stringBuilder.toString();
    }

    /**
     * Loads help links from the file helpLinks.csv and removes links without reference
     * 
     * @param input - user's input
     * @param stringBuilder - StringBuilder class with append and insert methods
     * @see StringBuilder
     */
	public void getLinks(String input, StringBuilder stringBuilder) {
		ArrayList<String> cfunct = Lists.newArrayList(Statistics
                .getFunctionCalls(input));

        if(helpLinks == null)
            loadLinks();

		//TODO remove links without reference
		if (cfunct.size() > 0) {
			stringBuilder.append("<ul>");
			for (String functionName : cfunct) {
				if (helpLinks.containsKey(functionName)) {
					stringBuilder.append("<li>").append(functionName).append(": ")
							.append("<a href=\"").append(helpLinks.get(functionName))
							.append("\">link</a> ").append("</li>");
				}
			}
			stringBuilder.append("</ul>");
		}
	}

    /**
     * Loads additional libraries into the jruby engine.
     * @param calculatorEngine engine to load libraries into
     */
    private void loadLibraries(CalculatorEngine calculatorEngine) {
        try {
            calculatorEngine
                    .evalFile(getServletContext().getResourceAsStream(RUBY_LIBS_PATH));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }

    /**
     * Loads the help links from file.
     */
    private void loadLinks() {
        BufferedReader reader =
                new BufferedReader(
                        new InputStreamReader(
                                getServletContext()
                                        .getResourceAsStream(HELP_LINKS_PATH)));

        helpLinks = parseHelpLinksFile(reader);
    }

    /**
     * Parses a csv file that is specified with the stream. Extracts the link
     * mappings into a HashMap and returns it.
     * @param reader stream from the file
     * @return a HashMap containing the mappings of function names to help links
     */
    private HashMap<String, String> parseHelpLinksFile(BufferedReader reader) {
        HashMapBuilder<String, String> hashMapBuilder = new HashMapBuilder<String, String>();
        String line;

        try {
            while((line = reader.readLine()) != null) {
                hashMapBuilder.put(line.split(",")[0], line.split(",")[1]);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return hashMapBuilder.build();
    }
}
